﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;

using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;

namespace Client
{
    public partial class FormClient : Form
    {
        //define opCode;
        const int LOGIN = 0;
        const int REG = 1;
        const int EXIT = 2;
        const int LOGINSUCCESSFULL = 3;
        const int UPDATECLIENTLIST = 4;
        const int SHARE = 5;
        const int SEARCH = 6;
        const int DOWNLOAD = 7;
        const int LOGOUT = 8;
        const int LOGINFAIL = 9;
        const int REQUEST_DOWNLOAD = 10;
        const int TRACK_AVAILABLE = 11;
        const int TRACK_NOTAVAILABLE = 12;
        const int READY_DOWNLOAD = 13;

        public FormClient()
        {
            InitializeComponent();
        }
        private FormConnect formConnect = new FormConnect();
        private FormLogin formLogin;
        private FormREG formReg;


        private TcpClient tcpClient;
        private static int port = 1234;

        List<string> IPDownLoadList = new List<string>();

        private bool flagConnected = false;
        private Thread myThread = null;
        private bool threadStop = false;
        private void connectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            formConnect.ShowDialog();
            flagConnected = formConnect.getConnectionStatus();
            if (flagConnected)
            {
                tcpClient = formConnect.getTcpClient();
                string IP = formConnect.getServerIP();
                myThread = new Thread(new ThreadStart(receive));
                myThread.Start();
                loginToolStripMenuItem.Enabled = true;
                regToolStripMenuItem.Enabled = true;
                connectToolStripMenuItem.Enabled = false;
            }
        }

        private delegate void updateGuiHandle();
        private void updateGui()
        {
            if(this.InvokeRequired)
            {
                this.Invoke(new updateGuiHandle(updateGui), new object[] { });
                return;
            }
            formLogin.Close();
            tabControl.Enabled = true;
            loginToolStripMenuItem.Enabled = false;
            regToolStripMenuItem.Enabled = false;
            logOutToolStripMenuItem.Enabled = true;
        }


        private void loginToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //if (myThread != null)
            //{
            //    myThread.Abort();
            //}
            formLogin = new FormLogin(tcpClient);
            formLogin.ShowDialog();

            if (formLogin.getLoginStatus())
            {
                this.flagConnected = true;

                regToolStripMenuItem.Enabled = false;
                loginToolStripMenuItem.Enabled = false;
                logOutToolStripMenuItem.Enabled = true;
                tabControl.Enabled = true;
            }
        }

        private delegate void updateProgressBarHandle(int value);
        private void updateProgressBar(int value)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new updateProgressBarHandle(updateProgressBar), new object[] { value });
                return;
            }
            progressBarDownloading.Value = value;
        }

        private void upLoad(object para)
        {
            object[] objectPara = (object[])para;
            int currentPort = (int)objectPara[0];
            string location = (string)objectPara[1];
            FileStream inStream = new FileStream(location, FileMode.Open, FileAccess.Read);
            try
            {
                TcpListener listener = new TcpListener(currentPort);
                listener.Start();
                TcpClient client = listener.AcceptTcpClient();
                NetworkStream ns = client.GetStream();
             
                long n = inStream.Length / 1024;
                long currentTotal = 0;
                byte[] buffer = new byte[1024];
                ns.Write(BitConverter.GetBytes(inStream.Length), 0, 4);//send length of file
                for (long i = 0; i < n; i++)
                {
                    currentTotal += inStream.Read(buffer, 0, 1024);
                    ns.Write(buffer, 0, 1024);
                    ns.Read(buffer, 0, 4);
                }
                int k = 0;
                k++;
                if (currentTotal < inStream.Length)
                {
                    buffer = new byte[1024];
                    int lastBlockSize = (int)(inStream.Length - currentTotal);
                    buffer = new byte[lastBlockSize];
                    inStream.Read(buffer, 0, buffer.Length);
                    ns.Write(BitConverter.GetBytes(buffer.Length), 0, 4);//send last block size
                    ns.Write(buffer, 0, lastBlockSize);
                }
            }
            catch (System.Exception ex)
            {
                inStream.Close();
                MessageBox.Show(ex.ToString());
            }

            inStream.Close();   

        }
        private void downLoad(object para)
        {
            object[] objectPara = (object[])para;
            int currentPort = (int)objectPara[0];
            TcpClient client = new TcpClient();
            IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(IPDownLoadList[0]), currentPort);
            try
            {
                client.Connect(ipe);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                return;
            }
            FileStream outStream = new FileStream(pathFile, FileMode.Create, FileAccess.Write);
            try
            {
                
                byte[] numberBuffer = new byte[4];
                NetworkStream ns = client.GetStream();
                ns.Read(numberBuffer, 0, 4);//read file length
                long fileLen = BitConverter.ToInt32(numberBuffer, 0);
                long n = fileLen / 1024;
                byte[] buffer = new byte[1024];
                long currentTotal = 0;
                int percent = 0;
                int current;
                for (long i = 0; i < n; i++)
                {
                    current = 0;
                    while (current<1024)
                    {
                        current += ns.Read(buffer, 0, 1024);
                    }
                    ns.Write(BitConverter.GetBytes(999), 0, 4);
                    outStream.Write(buffer, 0, 1024);
                    percent = (int)(100 * currentTotal / fileLen);
                    currentTotal += current;
                    updateProgressBar(percent);
                    
                }
                if (currentTotal < fileLen)
                {
                    ns.Read(numberBuffer, 0, 4);
                    int len = BitConverter.ToInt32(numberBuffer, 0);
                    buffer = new byte[len];
                    ns.Read(buffer, 0, len);
                    outStream.Write(buffer, 0, buffer.Length);
                    updateProgressBar(100);
                }
                updateProgressBar(0);
            
            }catch(Exception ex)
            {
                MessageBox.Show(ex.ToString());
                outStream.Close();
                return;
            }
            
            outStream.Close();
            MessageBox.Show("Xong roi heheheheh");


        }
        public void receive()
        {
            byte[] opCodeBuffer = new byte[4];
            NetworkStream ns = tcpClient.GetStream();

            while (ns.Read(opCodeBuffer, 0, 4) != 0 && !threadStop)
            {
                int opCode = BitConverter.ToInt32(opCodeBuffer, 0);
                switch (opCode)
                {
                    //case TRACK_AVAILABLE:
                    //    {
                    //        byte[] numberBuffer = new byte[4];
                    //        ns.Read(numberBuffer, 0, 4);
                    //        int port = BitConverter.ToInt32(numberBuffer, 0);
                    //    }
                    //    break;
                    case READY_DOWNLOAD:
                        {
                            byte[] numberBuffer = new byte[4];
                            ns.Read(numberBuffer, 0, 4);//read port of B
                            int portB = BitConverter.ToInt32(numberBuffer, 0);
                            Thread t = new Thread(new ParameterizedThreadStart(downLoad));
                            object[] para = new object[] { portB };
                            t.Start(para);
                        }
                        break;
                    case REQUEST_DOWNLOAD:
                        {
                            byte[] numberBuffer = new byte[4];
                            ns.Read(numberBuffer, 0, 4);//read length of location
                            int len = BitConverter.ToInt32(numberBuffer,0);
                            byte[] Buffer = new byte[len];
                            ns.Read(Buffer, 0, len);//read location music
                            string location = Encoding.ASCII.GetString(Buffer);
                            ns.Read(numberBuffer, 0, 4);//read length name of A;
                            len = BitConverter.ToInt32(numberBuffer, 0);
                            Buffer = new byte[len];
                            ns.Read(Buffer, 0, len);//read name of A
                            string AName = Encoding.ASCII.GetString(Buffer);
                            if (File.Exists(location))
                            {
                                ns.Write(BitConverter.GetBytes(TRACK_AVAILABLE), 0, 4);

                                Thread t = new Thread(new ParameterizedThreadStart(upLoad));
                                port++;
                                ns.Write(BitConverter.GetBytes(port), 0, 4);//send port
                                ns.Write(BitConverter.GetBytes(AName.Length), 0, 4);//write length of name of A
                                ns.Write(Encoding.ASCII.GetBytes(AName), 0, AName.Length);//write name of A
                                object[] para = new object[] { port, location };
                                t.Start(para);
                            }
                            else
                            {
                                ns.Write(BitConverter.GetBytes(TRACK_NOTAVAILABLE), 0, 4);
                            }
                            
                        }
                        break;
                    case LOGINFAIL:
                        {
                            MessageBox.Show("Error!\rLogin is not successful!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                        break;
                    case LOGINSUCCESSFULL:
                        {
                            MessageBox.Show("Login is successful!", "Notify", MessageBoxButtons.OK, MessageBoxIcon.Information);
                            updateGui();
                            //tabControl.Enabled = true;
                            
                        }
                        break;
                    case SEARCH:
                        {
                            ////////////////////////////receive result//////////////////////////////////
                            //listViewResult.Items.Clear();
                            
                            byte[] numberBuffer = new byte[4];
                            ns.Read(numberBuffer, 0, 4);//read number of client have that track
                            int n = BitConverter.ToInt32(numberBuffer, 0);
                            for (int i = 0; i < n; i++)
                            {
                                ns.Read(numberBuffer, 0, 4);//read length of client name
                                int len = BitConverter.ToInt32(numberBuffer, 0);
                                byte[] buffer = new byte[len];
                                ns.Read(buffer, 0, len);//read client name
                                string name = Encoding.ASCII.GetString(buffer);

                                ns.Read(numberBuffer, 0, 4);//read length of IP
                                len = BitConverter.ToInt32(numberBuffer, 0);
                                buffer = new byte[len];
                                ns.Read(buffer, 0, len);//read IP
                                string IP = Encoding.ASCII.GetString(buffer);

                                //add to listview
                                ListViewItem lvItem = new ListViewItem(name);
                                lvItem.SubItems.Add(IP);
                                doAddItemToListView(listViewResult, lvItem);
                            }
                        }
                        break;
                    case UPDATECLIENTLIST:
                        {
                            //for(int i=0; i<listViewClientList.Items.Count; i++)
                            {
                                doRemoveItemFromListView();
                            }
                            byte[] lenBuffer = new byte[4];
                            byte[] buffer;
                            ns.Read(lenBuffer, 0, 4);//read number of clients
                            int n = BitConverter.ToInt32(lenBuffer, 0);
                            for (int i = 0; i < n; i++)
                            {
                                ns.Read(lenBuffer, 0, 4);//read length of client name i
                                int len = BitConverter.ToInt32(lenBuffer, 0);
                                buffer = new byte[len];
                                ns.Read(buffer, 0, len);//read username
                                string userName = Encoding.ASCII.GetString(buffer, 0, len);

                                ns.Read(lenBuffer, 0, 4);//read length of client IP i
                                len = BitConverter.ToInt32(lenBuffer, 0);
                                buffer = new byte[len];
                                ns.Read(buffer, 0, len);//read IP
                                string IP = Encoding.ASCII.GetString(buffer, 0, len);

                                ListViewItem item = new ListViewItem(IP);
                                item.SubItems.Add(userName);
                                //listViewClientList.Items.Add(item);
                                doAddItemToListView(listViewClientList, item);
                            }
                        }
                        break;
                    default:

                        break;
                }
            }
        }

        private delegate void addItemToListViewDelegate(ListView lv, ListViewItem item);
        private delegate void removeItemFromListViewDelegate();
        public void doAddItemToListView(ListView lv, ListViewItem item)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new addItemToListViewDelegate(doAddItemToListView),
                            new object[] { lv, item });
                return;
            }
            lv.Items.Add(item);

        }
        public void doRemoveItemFromListView()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new removeItemFromListViewDelegate(doRemoveItemFromListView));
                return;
            }
            listViewClientList.Items.Clear();

        }
        private void regToolStripMenuItem_Click(object sender, EventArgs e)
        {
            formReg = new FormREG(tcpClient);
            formReg.ShowDialog();

        }

        private void FormClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (flagConnected)
            {
                threadStop = true;
                NetworkStream ns = tcpClient.GetStream();
                ns.Write(BitConverter.GetBytes(EXIT), 0, 4);
                Environment.Exit(0);
            }
        }



        private void FormClient_Load(object sender, EventArgs e)
        {
            comboBoxSearchType.SelectedIndex = 0;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (flagConnected)
            {
                threadStop = true;
                NetworkStream ns = tcpClient.GetStream();
                ns.Write(BitConverter.GetBytes(EXIT), 0, 4);

            }
            this.Close();
            Environment.Exit(0);
        }

        private void logOutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            loginToolStripMenuItem.Enabled = true;
            regToolStripMenuItem.Enabled = true;
            logOutToolStripMenuItem.Enabled = false;
            NetworkStream ns = tcpClient.GetStream();
            ns.Write(BitConverter.GetBytes(LOGOUT), 0, 4);//send opcode
            tabControl.Enabled = false;
            listViewClientList.Items.Clear();
            //myThread.Abort();
            //myThread = null;
        }

        private void buttonSearch_Click(object sender, EventArgs e)
        {
            NetworkStream ns = tcpClient.GetStream();
            ns.Write(BitConverter.GetBytes(SEARCH), 0, 4);//send opcode

            ns.Write(BitConverter.GetBytes(textBoxSearch.Text.Length), 0, 4);//send length of search string
            ns.Write(Encoding.ASCII.GetBytes(textBoxSearch.Text), 0, textBoxSearch.Text.Length);//send search string

            int searchType = comboBoxSearchType.SelectedIndex;
            /*searchType:
             * 0    Title
             * 1    Artist
             * 2    Composer
             * 3    Album
             */
            ns.Write(BitConverter.GetBytes(searchType), 0, 4);//send search type;

        }

        private void buttonAdd_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Multiselect = true;
            dlg.Title = "Choose file music";
            dlg.Filter = "*.mp3|*.mp3";
           
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                foreach (String file in dlg.FileNames)
                {
                    ListViewItem lvItem = new ListViewItem(file);
                    listViewFileAdded.Items.Add(lvItem);
                }
               
            }
        }

        private void buttonRemove_Click(object sender, EventArgs e)
        {
            while(listViewFilesToShare.SelectedItems.Count>0)
            {
                ListViewItem item = listViewFilesToShare.SelectedItems[0];
                    listViewFilesToShare.Items.Remove(item);
            }
        }
        int nFileBeShared;
        private void buttonShare_Click(object sender, EventArgs e)
        {
            NetworkStream ns = tcpClient.GetStream();
            ns.Write(BitConverter.GetBytes(SHARE), 0, 4);//send opcode
            ns.Write(BitConverter.GetBytes(listViewFilesToShare.Items.Count), 0, 4);//send number of files to share

            try
            {
                if (listViewFilesToShare.Items.Count > 0)
                {
                    foreach (ListViewItem item in listViewFilesToShare.Items)
                    {
                        TagLib.File fileTag = TagLib.File.Create(item.Text);
                        nFileBeShared++;
                        string title = fileTag.Tag.Title;
                        string album = fileTag.Tag.Album;
                        string composer = "";
                        if (fileTag.Tag.Composers.Length > 0)
                        {
                            composer = fileTag.Tag.Composers[0];
                        }
                        string artist = "";
                        if (fileTag.Tag.Artists.Length > 0)
                        {
                            artist = fileTag.Tag.Artists[0];
                        }

                        //send title
                        ns.Write(BitConverter.GetBytes(title.Length), 0, 4);//send length of title
                        ns.Write(Encoding.ASCII.GetBytes(title), 0, title.Length);//send title
                        //send album name
                        ns.Write(BitConverter.GetBytes(album.Length), 0, 4);//send length of title
                        ns.Write(Encoding.ASCII.GetBytes(album), 0, album.Length);//send album
                        //send composer
                        ns.Write(BitConverter.GetBytes(composer.Length), 0, 4);//send length of composer
                        ns.Write(Encoding.ASCII.GetBytes(composer), 0, composer.Length);//send composer
                        //send artist
                        ns.Write(BitConverter.GetBytes(artist.Length), 0, 4);//send length of artist
                        ns.Write(Encoding.ASCII.GetBytes(artist), 0, artist.Length);//send artist
                        //send location
                        ns.Write(BitConverter.GetBytes(item.Text.Length), 0, 4);//send length of location
                        ns.Write(Encoding.ASCII.GetBytes(item.Text), 0, item.Text.Length);//send location
                        listViewFilesToShare.Items.Remove(item);
                    }
                    MessageBox.Show("Share file is successful!\rHave "+nFileBeShared.ToString()+" file ", "Notify", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("Error!\r" + ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                //MessageBox.Show(ex.ToString());
            }
           
            
        }
        private string pathFile = "";
        private void buttonDownload_Click(object sender, EventArgs e)
        {
            if (listViewResult.SelectedItems.Count > 0)
            {
                FolderBrowserDialog dlg = new FolderBrowserDialog();
                string strFolder = "";
                if (dlg.ShowDialog()==DialogResult.OK)
                {
                    strFolder = dlg.SelectedPath;
                }
                while (listViewResult.SelectedItems.Count > 0)
                {
                    ListViewItem item = listViewResult.SelectedItems[0];
                    //ListViewItem lvItem = new ListViewItem(file);
                    //listViewFileAdded.Items.Add(lvItem);
                    pathFile += strFolder+"\\"+item.SubItems[0].Text+".mp3";
                    ListViewItem lvItem = new ListViewItem(pathFile);
                    IPDownLoadList.Add(item.SubItems[1].Text);
                    
                    NetworkStream ns = tcpClient.GetStream();
                    ns.Write(BitConverter.GetBytes(REQUEST_DOWNLOAD), 0, 4);//send opcode
                    ns.Write(BitConverter.GetBytes(item.Text.Length), 0, 4);//send length of client name B
                    ns.Write(Encoding.ASCII.GetBytes(item.Text), 0, item.Text.Length);//send name of client B
                    ns.Write(BitConverter.GetBytes(textBoxSearch.Text.Length), 0, 4);//send length of track name
                    ns.Write(Encoding.ASCII.GetBytes(textBoxSearch.Text), 0, textBoxSearch.Text.Length);//send track name
                    listViewResult.Items.Remove(item);
                    listViewDownloadedFile.Items.Add(lvItem);
                }
                

            }
            else
            {
                MessageBox.Show("Ngu ha may");
            }

        }

        private void buttonAddToShare_Click(object sender, EventArgs e)
        {
            while (listViewFileAdded.SelectedItems.Count > 0)
            {
                ListViewItem item = listViewFileAdded.SelectedItems[0];
                listViewFileAdded.Items.Remove(item);
                listViewFilesToShare.Items.Add(item);

            }
        }

        private void buttonReturn_Click(object sender, EventArgs e)
        {
            while ( listViewFilesToShare.SelectedItems.Count>0)
            {
                ListViewItem item = listViewFilesToShare.SelectedItems[0];
                listViewFilesToShare.Items.Remove(item);
                listViewFileAdded.Items.Add(item);

            }
        }

        private void tabPage1_Click(object sender, EventArgs e)
        {

        }


    }
}
